/* MarkusCrawler
 *
 * Copyright (C) 2011 by Markus '[mackan]' Hyttinen <mackanhyttinen@gmail.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 */

package me.crawler;
public class CrawlerController {


	public final static int DEFAULT_MAX_THREADS = 200;
	protected int maxThreads = DEFAULT_MAX_THREADS ;
	protected CrawlerQueue queue;
	protected CrawlerQueue proccesedQueue;
	protected int counter = 0;
	protected int threadsRunning = 0;
	protected int proccesed = 0;
	protected CrawlerDBManager dbm;  
	protected CrawlerFilter filter;
	protected CrawlerListener crawlerListener;

	




	public CrawlerController(CrawlerQueue queue) {
		this.queue = queue;
		proccesedQueue = new CrawlerQueue();
		filter = new CrawlerFilter();

	}
	
	public void start(){
		
		try{
				
		while (threadsRunning > 0 || !queue.isEmpty())	{
			
		if (!queue.isEmpty())
			this.startThreads();
			
			
		
		}
		///finaly flushes to db if some pages still is in the proccessed queue
		if(!proccesedQueue.isEmpty()){
			if(crawlerListener != null)
				crawlerListener.flushingToDatabase();
			
			dbm.flushToDB(proccesedQueue.clone());		
		}
		if(crawlerListener != null)
			crawlerListener.finished();
		System.out.println("DONE");
		
		} catch (InstantiationException e) {	
			e.printStackTrace();

		} catch (IllegalAccessException e) {
			e.printStackTrace();}
	
		
	}

	public synchronized void threadFinished(int threadId) {
		
		threadsRunning--;
		if (threadsRunning == 0) {

			if (queue.size() == 0) {		
				return;
			}
			try {
				startThreads();
			} catch (InstantiationException e) {
			} catch (IllegalAccessException e) {
			}
		}
	}
	



	public synchronized void startThreads()
		throws InstantiationException, IllegalAccessException {
		int max = maxThreads - threadsRunning;
		int ts = queue.size();
		if (ts < max || maxThreads == -1) {
			max = ts;
		}
		for (int n = 0; n < max; n++) {
			CrawlerThread thread = new CrawlerThread();
			thread.setCrawlerController(this);
			thread.setQueue(queue);
			thread.setId(threadsRunning++);
			thread.setFilter(this.filter.clone());
			thread.start();
		}
		if(crawlerListener != null)
			crawlerListener.threadsRunning(threadsRunning);
	}

	public void setCrawlerDBManager(CrawlerDBManager crawlerDBManager){
		this.dbm = crawlerDBManager;
	}
	
	
	public synchronized void AddProccesedItem(CrawlerItem crawlerItem){
		
		if (crawlerItem instanceof CrawlerHTMLPage){
			proccesedQueue.push((CrawlerHTMLPage)crawlerItem);
			proccesed++;
			if(crawlerListener != null){
			crawlerListener.proccesedAmount(proccesed);
			crawlerListener.proccesedPage(crawlerItem.getUrl().toString());
			}
			System.out.println("PROCCESED : " + proccesed);
			
			if(dbm != null){
				if (proccesedQueue.size() > dbm.getFlushInterval()){
					System.err.println("FLUSHING");
					dbm.flushToDB(proccesedQueue.clone());			
					proccesedQueue.clear();
				}	
			}
		}
	}


	/**
	 * @return the filter
	 */
	public synchronized CrawlerFilter getFilter() {
		return filter;
	}

	/**
	 * @param filter the filter to set
	 */
	public synchronized void setFilter(CrawlerFilter filter) {
		this.filter = filter;
	}

	/**
	 * @return the maxThreads
	 */
	public synchronized int getMaxThreads() {
		return maxThreads;
	}

	/**
	 * @param maxThreads the maxThreads to set
	 */
	public synchronized void setMaxThreads(int maxThreads) {
		this.maxThreads = maxThreads;
	}

	/**
	 * @return unique number generated by controller
	 */
	public synchronized int getUniqueNumber() {
		return ++counter;
	}

	/**
	 * @return the threadsRunning
	 */
	public synchronized int getThreadsRunning() {
		return threadsRunning;
	}
	
	/**
	 * @return the crawlerListener
	 */
	public synchronized CrawlerListener getCrawlerListener() {
		return crawlerListener;
	}

	/**
	 * @param crawlerListener the crawlerListener to set
	 */
	public synchronized void setCrawlerListener(CrawlerListener crawlerListener) {
		this.crawlerListener = crawlerListener;
	}
	
	
}
